package com.basic.business.order.server;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.basic.business.common.constants.KmConstants;
import com.basic.business.common.utils.DateUtils;
import com.basic.business.common.utils.DingTalkUtils;
import com.basic.business.common.utils.MonitoredTask;
import com.basic.business.common.utils.SpringUtils;
import com.basic.business.common.utils.kmUtil.PostParamsUtil;
import com.basic.business.order.domain.TOrdersMain;
import com.basic.business.order.domain.TimeRange;
import com.basic.business.order.service.ITOrdersMainService;
import com.basic.business.redis.server.SyncOrdersToRedisServer;
import com.basic.business.rpaServer.RPAServer;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.ConnectException;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 获取订单数据
 *
 * @author zhaoxy_jn@163.com
 */
@Slf4j
@Component
public class GetOrdersDataServer {

    private static final double MAX_REQUESTS_PER_MINUTE = 30.0;

    private volatile TimeRange currentTimeRange;

    // 当前页码
    private volatile String currentPageNo = "1";

    // 限流器配置
    private static final RateLimiter rateLimiter = RateLimiter.create(MAX_REQUESTS_PER_MINUTE / 60);

    private ScheduledFuture<?> scheduledTask;

    // 线程安全的执行器服务
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    @Resource
    private RPAServer rpaServer;

    @Resource
    private SyncOrdersToRedisServer syncOrdersToRedisServer;

    @Resource
    private ITOrdersMainService mainService;

    // 添加重试计数器
    private final ConcurrentMap<String, Integer> retryCountMap = new ConcurrentHashMap<>();
    public static final int MAX_RETRY_COUNT = 3; // 最大重试次数
    public static final long BASE_RETRY_DELAY = 3_000; // 基础延迟时间(ms)

    /**
     * 初始化
     */
    public void init(){
        long startTime = DateUtils.dateToTimestamp("2025-06-17 15:05:00");
        // 获取存储的最后更新时间
        Long lastUpdTime = mainService.getLastUpdTime();
        if (Objects.nonNull(lastUpdTime)) {
            startTime = DateUtils.getZeroTimestamp(lastUpdTime);
        }

        currentTimeRange = new TimeRange(
                DateUtils.timestampToDate(startTime),
                DateUtils.timestampToDate(startTime + 59_999)
        );
        // 初始化页码
        currentPageNo = "1";

        long delay = calculateDelay(currentTimeRange);
        scheduleNextTask(delay);

    }

    /**
     * 调度下一个任务
     * @param delay 初始延迟时间（毫秒）
     */
    private void scheduleNextTask(long delay) {
        if (scheduledTask != null && !scheduledTask.isDone()) {
            scheduledTask.cancel(false);
        }

        // 确保延迟不小于0
        long safeDelay = Math.max(delay, 0);

        scheduledTask = executor.schedule(this::processCurrentTimeRange, safeDelay, TimeUnit.MILLISECONDS);
    }

    /**
     * 处理当前时间范围
     */
    private void processCurrentTimeRange() {
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        // 使用当前时间范围和页码
        final TimeRange processingTimeRange = currentTimeRange;
        final String pageNo = currentPageNo;
        String[] timeRangeArr = processingTimeRange.toArray();
        // 日志记录
        String logInfo = null;
        try {
            rateLimiter.acquire();
            Map<String, String> params = Maps.newHashMap();
            params.put("startTime", timeRangeArr[0]);
            params.put("endTime", timeRangeArr[1]);
            String paramJson = JSON.toJSONString(PostParamsUtil.getBusinessParams(pageNo, KmConstants.QUERY_OUTSTOCK_SIMPLE_METHOD, params));
            String result = HttpUtil.post(KmConstants.URL, paramJson);
            JSONObject jsonResult = JSON.parseObject(result);
            if (!jsonResult.getBoolean("success")) {
                log.error("API请求时间范围{},页码：{},流水号：{}；失败: {}",
                        timeRangeArr, pageNo, jsonResult.getString("traceId"), jsonResult.getString("msg"));
                // 失败后推进时间范围并重置页码
                advanceTimeRangeAndResetPage();
                return;
            }
            // 获取traceId
            String traceId = jsonResult.getString("traceId");
            // 解析订单并入库
            logInfo = parseAndSaveOrders(jsonResult, timeRangeArr, traceId);

            boolean hasNext = jsonResult.getBoolean("hasNext");
            if (hasNext) {
                // 还有下一页：更新页码并立即调度下一页
                currentPageNo = String.valueOf(Integer.parseInt(pageNo) + 1);
                scheduleNextTask(0); // 立即调度下一页
            } else {
                // 没有下一页：推进时间范围并重置页码
                advanceTimeRangeAndResetPage();
            }

        } catch (Exception e) {
            boolean isTimeout = e instanceof IORuntimeException
                    && e.getCause() instanceof ConnectException
                    && e.getMessage().contains("连接超时");

            log.error("API请求异常 - 时间范围: {} 类型: {} 详情: {}",
                    Arrays.toString(timeRangeArr),
                    isTimeout ? "连接超时" : e.getClass().getSimpleName(),
                    e.getMessage());

            // 特殊处理超时情况
            if (isTimeout) {
                String timeRangeKey = Arrays.toString(timeRangeArr);
                int retryCount = retryCountMap.getOrDefault(timeRangeKey, 0);

                if (retryCount < MAX_RETRY_COUNT) { // 最大重试3次
                    retryCountMap.put(timeRangeKey, retryCount + 1);
                    long delay = (retryCount + 1) * BASE_RETRY_DELAY; // 递增延迟：3秒、6秒、9秒
                    log.warn("检测到连接超时(重试{}/3)，将在{}ms后重试...", retryCount + 1, delay);
                    scheduleNextTask(delay);
                    return;
                } else {
                    retryCountMap.remove(timeRangeKey); // 重置计数器
                    log.error("连续3次重试均超时，放弃处理时间范围：{}", timeRangeKey);
                    // 异步发送钉钉消息
                    CompletableFuture.runAsync(() -> {
                        DingTalkUtils.send(
                                String.format("【严重告警】API连续超时 \n【时间范围】%s \n【重试次数】3次均失败 \n【最后错误】%s",
                                        Arrays.toString(timeRangeArr),
                                        e.getMessage()),
                                "RPA",
                                ""
                        );
                    });
                }

            }

            // 异常后推进时间范围并重置页码
            advanceTimeRangeAndResetPage();
        } finally {
            if (logInfo != null) {
                logInfo += String.format("总耗时 %s ms；", System.currentTimeMillis() - startTime);
                log.info(logInfo);
            }
        }
    }

    /**
     * 推进时间范围并重置页码
     */
    private void advanceTimeRangeAndResetPage() {
        // 推进到下一个时间范围
        currentTimeRange = calculateNextTimeRange();
        // 重置页码
        currentPageNo = "1";
        // 调度下一个时间范围
        long delay = calculateDelay(currentTimeRange);
        scheduleNextTask(delay);
    }

    /**
     * 解析并保存订单
     */
    private String parseAndSaveOrders(JSONObject jsonResult, String[] timeRange, String traceId) {
        try {
            // 解析订单
            List<TOrdersMain> orders = ParseOrders.init(jsonResult);
            if (orders.isEmpty()) {
                return String.format("流水号: %s；时间范围：%s；页码: %s；订单为空；", traceId, Arrays.toString(timeRange), currentPageNo);
            }
            long dbStart = System.currentTimeMillis();
            // 保存订单
            int cnt = SpringUtils.getBean(ITOrdersMainService.class).batchInsertOrUpdate(orders);
            long dbEnd = System.currentTimeMillis();
            // 同步Redis信息
            syncOrdersToRedisServer.init(orders);
            // 自动处理程序入口
            rpaServer.init(orders);
            return String.format("流水号: %s；时间范围：%s；页码: %s；入库%s条；耗时：%s ms；", traceId, Arrays.toString(timeRange), currentPageNo, cnt, dbEnd - dbStart);
        } catch (Exception e) {
            log.error("后续操作处理失败：时间范围: {}；页码: {}；", Arrays.toString(timeRange), currentPageNo, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 计算下一个时间范围
     */
    private TimeRange calculateNextTimeRange() {
        long nextStart = DateUtils.dateToTimestamp(currentTimeRange.startTime) + 60_000;
        return new TimeRange(
                DateUtils.timestampToDate(nextStart),
                DateUtils.timestampToDate(nextStart + 59_999)
        );
    }

    /**
     * 计算需要延迟的时间
     */
    private long calculateDelay(TimeRange nextRange) {
        long nextStartTime = DateUtils.dateToTimestamp(nextRange.startTime);
        long now = System.currentTimeMillis() - 60_000;
        long delay = nextStartTime > now ? nextStartTime - now : 0;
        // 增加5秒延迟，防止订单数据丢失
        return delay + 5_000;
    }

    @PreDestroy
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }

        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }


}
